package com.weixin.common.sftp;

import com.jcraft.jsch.ChannelSftp.LsEntry;
import com.jcraft.jsch.SftpException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;

import java.io.InputStream;

/**
 * @author zh
 */
public class SftpTemplate {

    private final Logger log = LoggerFactory.getLogger(SftpTemplate.class);

    ThreadLocal<SftpClient> threadLocal = new ThreadLocal<>();
    private final SftpPool sftpPool;

    public SftpTemplate(SftpPool sftpPool) {
        this.sftpPool = sftpPool;
    }

    /**
     * 封装执行操作
     *
     * @param action
     * @return T
     * @author zh
     */
    private <T> T execute(SftpCallback<T> action) throws SftpException {
        Assert.notNull(action, "Callback object must not be null");
        String hostName = sftpPool.isUniqueHost() ? null : HostHolder.getHostName();
        SftpClient sftpClient = null;
        try {
            sftpClient = sftpPool.borrowObject(hostName);
            return action.doInSftp(sftpClient.getChannelSftp());
        } finally {
            HostHolder.clear();
            if (sftpClient != null) {
                if (sftpClient.reset()) {
                    sftpPool.returnObject(hostName, sftpClient);
                } else {
                    sftpPool.invalidateObject(hostName, sftpClient);
                }
            }
        }
    }

    /**
     * 封装执行操作
     *
     * @param action
     * @return T
     * @author zh
     */
    private void executeWithoutResult(SftpCallbackWithoutResult action) throws SftpException {
        Assert.notNull(action, "Callback object must not be null");
        this.execute(channelSftp -> {
            action.doInSftp(channelSftp);
            return null;
        });
    }

    /**
     * 封装执行操作（需要手动返回连接对象）
     *
     * @param action
     * @return T
     * @author zh
     * @see #returnSftpClient()
     */
    private <T> T executeNoReturnObj(SftpCallback<T> action) throws SftpException {
        Assert.notNull(action, "Callback object must not be null");
        String hostName = sftpPool.isUniqueHost() ? null : HostHolder.getHostName();
        SftpClient sftpClient = sftpPool.borrowObject(hostName);
        threadLocal.set(sftpClient);
        return action.doInSftp(sftpClient.getChannelSftp());
    }

    /**
     * 手动归还连接对象
     *
     * @return void
     * @author zh
     */
    public void returnSftpClient() {
        String hostName = sftpPool.isUniqueHost() ? null : HostHolder.getHostName();
        try {
            SftpClient sftpClient = threadLocal.get();
            if (sftpClient != null) {
                if (sftpClient.reset()) {
                    sftpPool.returnObject(hostName, sftpClient);
                } else {
                    sftpPool.invalidateObject(hostName, sftpClient);
                }
            }
        } finally {
            HostHolder.clear();
            threadLocal.remove();
        }
    }

    /**
     * 下载到本地
     *
     * @param from 源路径  /upload/xxxx/xxx.jpg
     * @param to   下载路径 /data/xxxx
     * @return void
     * @author zh
     */
    public void download(String from, String to) throws SftpException {
        String hostName = HostHolder.getHostName();
        log.info("下载成文件开始：hostName:{}| fromPath :{} | toPath: {}", hostName, from, to);
        this.executeWithoutResult(channelSftp -> new ChannelSftpWrapper(channelSftp).download(from, to));
    }

    /**
     * 下载返回流(需要手动返回连接对象)
     *
     * @param from 源路径  /upload/xxxx/xxx.jpg
     * @return java.io.InputStream 返回流
     * @author zh
     * @see #returnSftpClient()
     */
    public InputStream download(String from) throws SftpException {
        String hostName = HostHolder.getHostName();
        log.info("下载成流开始：hostName:{},fromPath :{}", hostName, from);
        return this.executeNoReturnObj(channelSftp -> new ChannelSftpWrapper(channelSftp).download(from));
    }

    /**
     * 上传
     *
     * @param from 流
     * @param to   上到的路径 /upload/xxxx/xxx.jpg
     * @return void
     * @author zh
     */
    public void upload(InputStream from, String to) throws SftpException {
        log.info("上传流开始：toPath:{}", to);
        this.executeWithoutResult(channelSftp -> new ChannelSftpWrapper(channelSftp).upload(from, to));
    }

    /**
     * 上传
     *
     * @param from 源路径 /data/xxxx/xxx.jpg
     * @param to   上到的路径 /upload/xxxx/xxx.jpg
     * @return void
     * @author zh
     */
    public void upload(String from, String to) throws SftpException {
        log.info("上传文件开始：fromPath :{} | toPath:{}", from, to);
        this.executeWithoutResult(channelSftp -> new ChannelSftpWrapper(channelSftp).upload(from, to));
    }

    /**
     * 上传excel
     * modify20221201，报表是属于行方的sftp，行方的sftp目录是fundCode
     *
     * @param from 源路径 /xxx/xxxx/xxx.jpg
     * @param to   上到的路径 /fundCode/upload/xxxx/xxx.jpg
     * @return void
     * @author zh
     */
    public void uploadExcel(String from, String to) throws SftpException {
        upload(from, to);
    }

    /**
     * 判断文件存在
     *
     * @param path 源路径 /xxxx/xxxx/xxx.jpg
     * @return boolean
     * @author zh
     */
    public boolean exists(String path) throws SftpException {
        return this.execute(channelSftp -> new ChannelSftpWrapper(channelSftp).exists(path));
    }

    /**
     * 获取文件大小
     *
     * @param path 源路径 /upload/xxxx/xxx.jpg
     * @return long
     * @author zh
     */
    public long getSize(String path) throws SftpException {
        return this.execute(channelSftp -> new ChannelSftpWrapper(channelSftp).size(path));
    }

    /**
     * 获取文件列表
     *
     * @param path 源路径 /upload/xxxx/
     * @return LsEntry[]
     * @author zh
     */
    public LsEntry[] list(String path) throws SftpException {
        return this.execute(channelSftp -> new ChannelSftpWrapper(channelSftp).list(path));
    }
}
